package VLine

import (
	"bufio"
	"fmt"
	"os"
)

const (
	TOKEN_ID = iota
	TOKEN_STRING
	TOKEN_NUMBER
	TOKEN_ADD
	TOKEN_SUB
	TOKEN_MUL
	TOKEN_DIV
	TOKEN_MOD
	TOKEN_POS
	TOKEN_NEG
	TOKEN_POWER
	TOKEN_LEFTPARENTHESIS
	TOKEN_RIGHTPARENTHESIS
	TOKEN_LEFTBRACKET
	TOKEN_RIGHTBRACKET
	TOKEN_LEFTBRACE
	TOKEN_RIGHTBRACE
	TOKEN_ASSIGN
	TOKEN_SEMICOLON
	TOKEN_COMMA
	TOKEN_POINT
	TOKEN_CONCAT
	TOKEN_EQUAL
	TOKEN_NOTEQUAL
	TOKEN_LESS
	TOKEN_LESSEQUAL
	TOKEN_GREATER
	TOKEN_GREATEREQUAL

	TOKEN_MOVELEFTBIT
	TOKEN_MOVERIGHTBIT
	TOKEN_ANDBIT
	TOKEN_ORBIT
	TOKEN_NOTBIT

	TOKEN_EOF
	TOKEN_AND
	TOKEN_FALSE
	TOKEN_RETURN
	TOKEN_BREAK
	TOKEN_CONTINUE
	TOKEN_IF
	TOKEN_LET
	TOKEN_CONST
	TOKEN_STATIC
	TOKEN_NIL
	TOKEN_NOT
	TOKEN_OR
	TOKEN_TRUE
	TOKEN_FOR
	TOKEN_IN
	TOKEN_FUNCTION
	TOKEN_ELSEIF
	TOKEN_ELSE
	TOKEN_IMPORT
	TOKEN_FROM
)

type Scanner struct {
}

func (scanner *Scanner) ParserFile(path string) []*Token {
	var file, _ = os.Open(path)
	var buffer = bufio.NewReader(file)
	var value, _ = buffer.ReadSlice(0)
	return scanner.ParserBytes(value)
}

var keyword = map[string]int{
	"and":      TOKEN_AND,
	"false":    TOKEN_FALSE,
	"return":   TOKEN_RETURN,
	"break":    TOKEN_BREAK,
	"continue": TOKEN_CONTINUE,
	"con":      TOKEN_RETURN,
	"if":       TOKEN_IF,
	"let":      TOKEN_LET,
	"const":    TOKEN_CONST,
	"static":   TOKEN_STATIC,
	"nil":      TOKEN_NIL,
	"not":      TOKEN_NOT,
	"or":       TOKEN_OR,
	"true":     TOKEN_TRUE,
	"for":      TOKEN_FOR,
	"in":       TOKEN_IN,
	"function": TOKEN_FUNCTION,
	"elseif":   TOKEN_ELSEIF,
	"else":     TOKEN_ELSE,
	"import":   TOKEN_IMPORT,
	"from":     TOKEN_FROM,
}

const (
	STATE_START = iota
	STATE_NUMBER
	STATE_FLOAT
	STATE_STRING
	STATE_WORD
	STATE_NOTES
)

func (scanner *Scanner) ParserBytes(value []byte) []*Token {
	value = append(value, ' ')
	var array []*Token = make([]*Token, 0)
	var byteBuffer = make([]byte, 0)
	var state = STATE_START
	var line = 1
	var spacing = false
	for i := 0; i < len(value); i++ {
		switch state {
		case STATE_START:
			if (value[i] >= 'a' && value[i] <= 'z') || (value[i] >= 'A' && value[i] <= 'Z') || value[i] == '_' {
				byteBuffer = append(byteBuffer, value[i])
				state = STATE_WORD
			} else if value[i] >= '0' && value[i] <= '9' {
				byteBuffer = append(byteBuffer, value[i])
				state = STATE_NUMBER
			} else if value[i] == '"' {
				state = STATE_STRING
			} else if value[i] == '+' {
				array = append(array, TokenNew(TOKEN_ADD, "", spacing, line))
				spacing = false
			} else if value[i] == '-' {
				array = append(array, TokenNew(TOKEN_SUB, "", spacing, line))
				spacing = false
			} else if value[i] == '*' {
				array = append(array, TokenNew(TOKEN_MUL, "", spacing, line))
			} else if value[i] == '/' {
				if value[i+1] == '/' {
					state = STATE_NOTES
					i++
					spacing = false
				} else {
					array = append(array, TokenNew(TOKEN_DIV, "", spacing, line))
					spacing = false
				}
			} else if value[i] == '%' {
				array = append(array, TokenNew(TOKEN_MOD, "", spacing, line))
				spacing = false
			} else if value[i] == '^' {
				array = append(array, TokenNew(TOKEN_POWER, "", spacing, line))
				spacing = false
			} else if value[i] == '!' && value[i+1] == '=' {
				array = append(array, TokenNew(TOKEN_NOTEQUAL, "", spacing, line))
				spacing = false
				i++
			} else if value[i] == '=' {
				if value[i+1] == '=' {
					array = append(array, TokenNew(TOKEN_EQUAL, "", spacing, line))
					spacing = false
					i++
				} else {
					array = append(array, TokenNew(TOKEN_ASSIGN, "", spacing, line))
					spacing = false
				}
			} else if value[i] == '<' {
				if value[i+1] == '=' {
					array = append(array, TokenNew(TOKEN_LESSEQUAL, "", spacing, line))
					spacing = false
					i++
				} else if value[i+1] == '<' {
					array = append(array, TokenNew(TOKEN_MOVELEFTBIT, "", spacing, line))
					spacing = false
					i++
				} else {
					array = append(array, TokenNew(TOKEN_LESS, "", spacing, line))
					spacing = false
				}
			} else if value[i] == '>' {
				if value[i+1] == '=' {
					array = append(array, TokenNew(TOKEN_GREATEREQUAL, "", spacing, line))
					spacing = false
					i++
				} else if value[i+1] == '>' {
					array = append(array, TokenNew(TOKEN_MOVERIGHTBIT, "", spacing, line))
					spacing = false
					i++
				} else {
					array = append(array, TokenNew(TOKEN_GREATER, "", spacing, line))
					spacing = false
				}
			} else if value[i] == '.' {
				if value[i+1] == '.' {
					array = append(array, TokenNew(TOKEN_CONCAT, "", spacing, line))
					spacing = false
					i++
				} else {
					array = append(array, TokenNew(TOKEN_POINT, "", spacing, line))
					spacing = false
				}
			} else if value[i] == '(' {
				array = append(array, TokenNew(TOKEN_LEFTPARENTHESIS, "", spacing, line))
				spacing = false
			} else if value[i] == ')' {
				array = append(array, TokenNew(TOKEN_RIGHTPARENTHESIS, "", spacing, line))
				spacing = false
			} else if value[i] == '[' {
				array = append(array, TokenNew(TOKEN_LEFTBRACKET, "", spacing, line))
				spacing = false
			} else if value[i] == ']' {
				array = append(array, TokenNew(TOKEN_RIGHTBRACKET, "", spacing, line))
				spacing = false
			} else if value[i] == '{' {
				array = append(array, TokenNew(TOKEN_LEFTBRACE, "", spacing, line))
				spacing = false
			} else if value[i] == '}' {
				array = append(array, TokenNew(TOKEN_RIGHTBRACE, "", spacing, line))
				spacing = false

			} else if value[i] == '&' {
				if value[i+1] == '&' {
					array = append(array, TokenNew(TOKEN_AND, "", spacing, line))
					spacing = false
					i++
				} else {
					array = append(array, TokenNew(TOKEN_ANDBIT, "", spacing, line))
					spacing = false
				}
			} else if value[i] == '|' {
				if value[i+1] == '|' {
					array = append(array, TokenNew(TOKEN_OR, "", spacing, line))
					spacing = false
					i++
				} else {
					array = append(array, TokenNew(TOKEN_ORBIT, "", spacing, line))
					spacing = false
				}
			} else if value[i] == '!' {
				array = append(array, TokenNew(TOKEN_NOT, "", spacing, line))
				spacing = false
			} else if value[i] == '~' {
				array = append(array, TokenNew(TOKEN_NOTBIT, "", spacing, line))
				spacing = false
			} else if value[i] == ';' {
				array = append(array, TokenNew(TOKEN_SEMICOLON, "", spacing, line))
				spacing = false
			} else if value[i] == ',' {
				array = append(array, TokenNew(TOKEN_COMMA, "", spacing, line))
				spacing = false
			} else if value[i] == '\n' {
				line++
				spacing = false
			} else if value[i] == ' ' || value[i] == 9 {
				spacing = true
			}
		case STATE_STRING:
			if value[i] == '"' {
				array = append(array, TokenNew(TOKEN_STRING, string(byteBuffer), spacing, line))
				byteBuffer = make([]byte, 0)
				state = STATE_START
			} else if value[i] == '\\' {
				switch value[i+1] {
				case 'n':
					byteBuffer = append(byteBuffer, '\n')
				case 'b':
					byteBuffer = append(byteBuffer, '\b')
				case 't':
					byteBuffer = append(byteBuffer, '\t')
				case '"':
					byteBuffer = append(byteBuffer, '"')
				}
				i++
			} else {
				byteBuffer = append(byteBuffer, value[i])
			}
		case STATE_WORD:
			if (value[i] >= 'a' && value[i] <= 'z') || (value[i] >= 'A' && value[i] <= 'Z') || (value[i] >= '0' && value[i] <= '9') || value[i] == '_' {
				byteBuffer = append(byteBuffer, value[i])
			} else {
				var cage, ok = keyword[string(byteBuffer)]
				if ok {
					array = append(array, TokenNew(cage, "", spacing, line))
				} else {
					array = append(array, TokenNew(TOKEN_ID, string(byteBuffer), spacing, line))
				}
				byteBuffer = make([]byte, 0)
				i--
				spacing = false
				state = STATE_START
			}
		case STATE_NUMBER:
			if value[i] >= '0' && value[i] <= '9' {
				byteBuffer = append(byteBuffer, value[i])
			} else if value[i] == '.' {
				byteBuffer = append(byteBuffer, value[i])
				state = STATE_FLOAT
			} else {
				array = append(array, TokenNew(TOKEN_NUMBER, string(byteBuffer), spacing, line))
				byteBuffer = make([]byte, 0)
				i--
				spacing = false
				state = STATE_START
			}
		case STATE_FLOAT:
			if value[i] >= '0' && value[i] <= '9' {
				byteBuffer = append(byteBuffer, value[i])
			} else {
				array = append(array, TokenNew(TOKEN_NUMBER, string(byteBuffer), spacing, line))
				byteBuffer = make([]byte, 0)
				i--
				spacing = false
				state = STATE_START
			}
		}
	}
	array = append(array, TokenNew(TOKEN_SEMICOLON, "", spacing, line))
	array = append(array, TokenNew(TOKEN_EOF, "", spacing, line))
	for _, token := range array {
		fmt.Println(token)
	}
	return array
}
